package com.joyhonest.ruler;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;

import java.util.Calendar;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 刻度尺
 */
public class RulerView extends View {

    private ScaleMode currentScaleMode = ScaleMode.KEY_MINUTE;
    //除数，刻度精度
    private int divisor = 1;

    private List<PeriodTime> periodTimeList;

    private int rulerVisualWidth;
    private int rulerVisualHeight;

    private Paint rulerBoardPaint;
    private final int rulerBoardWidth = DensityUtil.dip2px(1);
    @ColorInt
    private int rulerBoardColor = Color.GREEN;
    /**
     * 时间指示器画笔
     */
    private Paint timeIndicatorPaint;
    @ColorInt
    private int timeIndicatorColor = Color.RED;
    private float timeIndicatorPosition = 0.5f;
    private float timeIndicatorPositionX;
    /**
     * 时间刻度画笔
     */
    private Paint rulerTickMarkPaint;
    @ColorInt
    private int rulerTickMarkColor = Color.GRAY;

    /**
     * 时间文本画笔
     */
    private Paint rulerTickTextPaint;
    @ColorInt
    private int rulerTickTextColor = Color.GRAY;
    private int textSize = DensityUtil.dip2px(12);
    /**
     * 时间段画笔
     */
    private Paint periodTimePaint;
    @ColorInt
    private int periodTimeColor = Color.YELLOW;

    //刻度起点位置
    private float tickStartDistance;
    //刻度尺刻度总长度
    private float rulerLength;
    //刻度尺刻度间隔
    private float rulerTickSpacing;
    private float scaleRatio;
    private boolean isDouble = false;
    private float beforeLength = 0;
    private float afterLength = 0;
    private float zoom = 0;
    private boolean isAutoScroll = false;
    private boolean canScrollBar = true;

    private float mLastx;
    private float mMove;
    private long timeIndicatorInMillis;
    private final long totalTimeInMillis = 24 * 60 * 60 * 1000;

    private Calendar baseCalendar;
    private long baseTimeInMillis;
    private Calendar currentCalendar;

    public RulerView(Context context) {
        this(context, null);
    }

    public RulerView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RulerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TimeRuler);
        rulerBoardColor = typedArray.getColor(R.styleable.TimeRuler_markColor, Color.GREEN);
        timeIndicatorColor = typedArray.getColor(R.styleable.TimeRuler_middleLineColor, Color.RED);
        timeIndicatorPosition = typedArray.getFloat(R.styleable.TimeRuler_middleLinePosition, 0.5f);
        periodTimeColor = typedArray.getColor(R.styleable.TimeRuler_periodTimeColor, Color.YELLOW);
        typedArray.recycle();

        initData();
        calculateDivisor();
        calculateTickStartDistance();
        calculateRulerTickSpacing();
        calculateRulerLength();
        calculateTimeIndicatorInMillis();
        initPaint();
    }

    private void initData() {
        baseCalendar = Calendar.getInstance();
        baseCalendar.set(Calendar.HOUR_OF_DAY, 0);
        baseCalendar.set(Calendar.MINUTE, 0);
        baseCalendar.set(Calendar.SECOND, 0);
        baseTimeInMillis = baseCalendar.getTimeInMillis();

        currentCalendar = Calendar.getInstance();
    }

    /**
     * 设置精度
     */
    private void calculateDivisor() {
        divisor = (currentScaleMode == ScaleMode.KEY_HOUR) ? 10 : 1;
    }

    private void calculateTickStartDistance() {
        tickStartDistance = rulerVisualWidth * timeIndicatorPosition;
    }

    /**
     * 根据{@link #zoom}和{@link #divisor}计算刻度间间隔。
     */
    private void calculateRulerTickSpacing() {
        rulerTickSpacing = ((320 + zoom) / (10f / divisor));
    }

    /**
     * 计算刻度尺刻度总长度
     */
    private void calculateRulerLength() {
        if (currentScaleMode == ScaleMode.KEY_HOUR) {
            rulerLength = rulerTickSpacing * 6 * 24;
        } else {
            rulerLength = rulerTickSpacing * 10 * 6 * 24;
        }
    }

    private void initPaint() {
        rulerBoardPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        rulerBoardPaint.setColor(rulerBoardColor);
        rulerBoardPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        rulerBoardPaint.setStrokeWidth(rulerBoardWidth);

        timeIndicatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        timeIndicatorPaint.setColor(timeIndicatorColor);
        timeIndicatorPaint.setStyle(Paint.Style.FILL);
        timeIndicatorPaint.setStrokeWidth(DensityUtil.dip2px(1));

        rulerTickMarkPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        rulerTickMarkPaint.setColor(rulerTickMarkColor);
        rulerTickMarkPaint.setStyle(Paint.Style.FILL);
        rulerTickMarkPaint.setStrokeWidth(DensityUtil.dip2px(1));

        rulerTickTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        rulerTickTextPaint.setColor(rulerTickTextColor);
        rulerTickTextPaint.setTextSize(textSize);

        periodTimePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        periodTimePaint.setColor(periodTimeColor);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        rulerVisualWidth = getMeasuredWidth();
        rulerVisualHeight = getMeasuredHeight();

        calculateTickStartDistance();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        drawRulerBoard(canvas);
        drawPeriodTime(canvas);
        drawRulerTickMark(canvas);
        drawMiddleLine(canvas);
        calculateTimeIndicatorInMillis();
    }

    /**
     * 绘制刻度尺边框
     */
    private void drawRulerBoard(Canvas canvas) {
        canvas.drawRect(0, 0, rulerVisualWidth, rulerVisualHeight, rulerBoardPaint);
    }

    /**
     * 绘制中间指示线，位置根据比例值得出
     */
    private void drawMiddleLine(Canvas canvas) {
        timeIndicatorPositionX = rulerVisualWidth * timeIndicatorPosition;
        canvas.drawLine(timeIndicatorPositionX, 0, timeIndicatorPositionX, rulerVisualHeight, timeIndicatorPaint);
    }

    private void drawRulerTickMark(Canvas canvas) {
        //小时模式：24小时 * 6
        //分钟模式：24小时 *  6 *  10
        canvas.save();
        //从时间指示器处开始绘制
        canvas.translate(tickStartDistance, 0);

        //先按分钟模式绘制
        if (currentScaleMode == ScaleMode.KEY_HOUR) {
            //小时级别画法
            for (int i = 0; i < 24 * 6 + 1; i++) {
                if (i % 6 == 0) {
                    //长刻度
                    canvas.drawLine(0, 0.5f * rulerVisualHeight, 0, rulerVisualHeight, rulerTickMarkPaint);
                    //刻度值
                    float timeStrWidth = rulerTickTextPaint.measureText(DateUtil.getHourMinute(i * divisor));
                    canvas.drawText(DateUtil.getHourMinute(i * divisor), -timeStrWidth / 2, 0.5f * rulerVisualHeight - 20, rulerTickTextPaint);
                } else {
                    //短刻度
                    canvas.drawLine(0, 0.7f * rulerVisualHeight, 0, rulerVisualHeight, rulerTickMarkPaint);
                }
                canvas.translate(rulerTickSpacing, 0);
            }
        } else {
            //分钟级别画法
            for (int i = 0; i < 24 * 10 * 6 + 1; i++) {
                if (i % 10 == 0) {
                    //长刻度
                    canvas.drawLine(0, 0.5f * rulerVisualHeight, 0, rulerVisualHeight, rulerTickMarkPaint);
                    //刻度值
                    float timeStrWidth = rulerTickTextPaint.measureText(DateUtil.getHourMinute(i * divisor));
                    canvas.drawText(DateUtil.getHourMinute(i * divisor), -timeStrWidth / 2, 0.5f * rulerVisualHeight - 20, rulerTickTextPaint);
                } else {
                    //短刻度
                    canvas.drawLine(0, 0.7f * rulerVisualHeight, 0, rulerVisualHeight, rulerTickMarkPaint);
                }
                canvas.translate(rulerTickSpacing, 0);
            }
        }
        canvas.restore();
    }

    private void drawPeriodTime(Canvas canvas) {
        if (periodTimeList == null) {
            return;
        }
        for (PeriodTime periodTime : periodTimeList) {
            float left = tickStartDistance +
                    (periodTime.getStartTime().getTimeInMillis() - baseTimeInMillis) / (totalTimeInMillis * 1.0f) * rulerLength;
            float right = tickStartDistance +
                    (periodTime.getStopTime().getTimeInMillis() - baseTimeInMillis) / (totalTimeInMillis * 1.0f) * rulerLength;
            canvas.drawRect(left, 0, right, rulerVisualHeight, periodTimePaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float xPosition = event.getX();

        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                //第一个手指按下
                isDouble = false;
                mLastx = xPosition;
                mMove = 0;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                //非第一个手指按下
                if (event.getPointerCount() == 2) {
                    //第二个手指按下
                    //双指按下时，需要静止滑动
                    setCanScrollBar(false);
                    beforeLength = (float) getDistance(event);
                    isDouble = true;
                    isAutoScroll = false;
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                //非第一个手指抬起
                break;
            case MotionEvent.ACTION_UP:
                //最后一个手指抬起
                if (isDouble) {
                    isDouble = false;
                    isAutoScroll = false;
                    //双指抬起
                    new Timer().schedule(new TimerTask() {
                        @Override
                        public void run() {
                            post(new Runnable() {
                                @Override
                                public void run() {
                                    //双指抬起时，需要解除静止滑动
                                    setCanScrollBar(true);
                                }
                            });
                        }
                    }, 100);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                //任意一个手指在移动
                if (event.getPointerCount() == 1 && !isDouble) {
                    //只有一个手指，且该手指在移动, 且在该手指按下与移动之间没有其他手指事件
                    mMove = xPosition - mLastx;
                    mLastx = xPosition;
                    tickStartDistance += mMove;
                    if (tickStartDistance >= timeIndicatorPositionX) {
                        tickStartDistance = timeIndicatorPositionX;
                    } else if (tickStartDistance + rulerLength <= timeIndicatorPositionX) {
                        tickStartDistance = (int) (timeIndicatorPositionX - rulerLength);
                    }
                    //计算时间指示器出对应的时间
                    calculateTimeIndicatorInMillis();
                    postInvalidate();
                } else if (event.getPointerCount() == 2) {
                    //有两个手指，且任意手指都可能在移动
                    afterLength = (float) getDistance(event);
                    if (beforeLength == 0) {
                        beforeLength = afterLength;
                    }
                    float gapLength = afterLength - beforeLength;
                    if (Math.abs(gapLength) > 5f) {
                        scaleRatio = afterLength / beforeLength;
                        //双指缩放了
                        beforeLength = afterLength;
                        onZooming();
                    }
                }
                break;
        }
        return true;
    }

    private void setCanScrollBar(boolean canScrollBar) {
        this.canScrollBar = canScrollBar;
    }

    private void calculateTimeIndicatorInMillis() {
        //指示器左边长度
        timeIndicatorInMillis = (long) ((timeIndicatorPositionX - tickStartDistance) / rulerLength * totalTimeInMillis);

        if (rulerChangeListener != null) {
            currentCalendar.setTimeInMillis(baseTimeInMillis + timeIndicatorInMillis);
            rulerChangeListener.onTimeChange(currentCalendar);
        }
    }

    private void onZooming() {
        if (scaleRatio > 1) {
            zoom += 15;
        } else {
            zoom -= 15;
        }

        zoom();
    }

    private void zoom() {
        if (zoom < -320 / 2) {
            currentScaleMode = ScaleMode.KEY_HOUR;
            divisor = 10;
            //小时级别了
            if (Math.abs(320 + zoom) < 30) {//不能小于10dp
                // 强制设置为小时级别的
                zoom = -320 + 30;
            }
        } else if (zoom < 320 * 1.5) {//不能超过1.5倍
            currentScaleMode = ScaleMode.KEY_MINUTE;
            divisor = 1;
            isAutoScroll = false;
            //分钟级别了
        } else {
            //已经是最大刻度
            currentScaleMode = ScaleMode.KEY_MINUTE;
            divisor = 1;
            zoom = 320 * 1.5f;
        }
        if (rulerChangeListener != null) {
            rulerChangeListener.onZoomChange(getZoomPercentByZoom());
        }
        float tempGap = timeIndicatorInMillis / (totalTimeInMillis * 1.0f) * rulerLength;

        calculateRulerTickSpacing();
        calculateRulerLength();

        //根据刻度尺缩放长度差值更新起始位置
        float gap = timeIndicatorInMillis / (totalTimeInMillis * 1.0f) * rulerLength;
        tickStartDistance += (tempGap - gap);

        postInvalidate();
    }

    /**
     * 根据当前缩放值计算缩放比例。
     *
     * @return 缩放比例 0-100
     */
    public int getZoomPercentByZoom() {
        int percent = 0;
        float maxZoom = 320 * 1.5f;
        float minZoom = -320 + 30;
        if (zoom < 0) {
            percent = (int) (-50 / minZoom * zoom + 50);
        } else {
            percent = (int) (50 / maxZoom * zoom + 50);
        }
        return percent;
    }

    /**
     * 根据给定缩放比例计算对应缩放值
     *
     * @param percent 缩放比例 0-100
     */
    private float getZoomByZoomPercent(int percent) {
        float zoom = 0;
        float maxZoom = 320 * 1.5f;
        float minZoom = -320 + 30;
        if (percent > 50) {
            zoom = ((percent - 50) * maxZoom) / 50;
        } else {
            zoom = ((50 - percent) * minZoom) / 50;
        }
        return zoom;
    }

    /**
     * 缩放刻度尺。
     *
     * @param percent 0-100
     */
    public void scale(int percent) {
        zoom = getZoomByZoomPercent(percent);

        zoom();
    }

    public void setCurrentCalendar(int year, int month, int day) {
        baseCalendar.set(Calendar.YEAR, year);
        baseCalendar.set(Calendar.MONTH, month);
        baseCalendar.set(Calendar.DAY_OF_MONTH, day);
        baseCalendar.set(Calendar.HOUR_OF_DAY, 0);
        baseCalendar.set(Calendar.MINUTE, 0);
        baseCalendar.set(Calendar.SECOND, 0);
        baseTimeInMillis = baseCalendar.getTimeInMillis();

        currentCalendar.setTimeInMillis(baseTimeInMillis);
    }

    /**
     * 设置数据并刷新页面
     */
    public void setPeriodTimeList(List<PeriodTime> periodTimeList) {
        this.periodTimeList = periodTimeList;
        postInvalidate();
    }

    public ScaleMode getCurrentScaleMode() {
        return currentScaleMode;
    }

    public void setCurrentScaleMode(ScaleMode currentScaleMode) {
        this.currentScaleMode = currentScaleMode;
        calculateDivisor();
    }

    /**
     * 计算两个手指之间的距离
     */
    private double getDistance(MotionEvent event) {
        float distanceX = Math.abs(event.getX(0) - event.getX(1));
        float distanceY = Math.abs(event.getY(0) - event.getY(1));
        return Math.sqrt(distanceX * distanceX + distanceY * distanceY);
    }

    /**
     * 刻度尺向前滚动给定秒数
     *
     * @param step 前进步数(单位：秒)
     */
    public void goForward(int step) {
        if (currentCalendar.getTimeInMillis() >= baseTimeInMillis + totalTimeInMillis - 1000) {
            return;
        }
        tickStartDistance -= (1000 * step) / (totalTimeInMillis * 1.0f) * rulerLength;
        postInvalidate();
    }

    /**
     * 刻度尺向后滚动给定秒数
     *
     * @param step 后退步数(单位：秒)
     */
    public void goBack(int step) {
        if (currentCalendar.getTimeInMillis() <= baseTimeInMillis) {
            return;
        }
        tickStartDistance += (1000 * step) / (totalTimeInMillis * 1.0f) * rulerLength;
        postInvalidate();
    }

    public interface OnRulerChangeListener {
        void onTimeChange(Calendar calendar);

        void onZoomChange(int percent);
    }

    private OnRulerChangeListener rulerChangeListener;

    public void setOnRulerChangeListener(OnRulerChangeListener mListener) {
        this.rulerChangeListener = mListener;
    }
}
